This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 243561
Collapse All | Expand All

(-)a/libs.asm/build.xml (+48 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!--
3
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
5
Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
6
7
Oracle and Java are registered trademarks of Oracle and/or its affiliates.
8
Other names may be trademarks of their respective owners.
9
10
11
The contents of this file are subject to the terms of either the GNU
12
General Public License Version 2 only ("GPL") or the Common
13
Development and Distribution License("CDDL") (collectively, the
14
"License"). You may not use this file except in compliance with the
15
License. You can obtain a copy of the License at
16
http://www.netbeans.org/cddl-gplv2.html
17
or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
18
specific language governing permissions and limitations under the
19
License.  When distributing the software, include this License Header
20
Notice in each file and include the License file at
21
nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
22
particular file as subject to the "Classpath" exception as provided
23
by Oracle in the GPL Version 2 section of the License file that
24
accompanied this code. If applicable, add the following below the
25
License Header, with the fields enclosed by brackets [] replaced by
26
your own identifying information:
27
"Portions Copyrighted [year] [name of copyright owner]"
28
29
Contributor(s):
30
31
The Original Software is NetBeans. The Initial Developer of the Original
32
Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
33
Microsystems, Inc. All Rights Reserved.
34
35
If you wish your version of this file to be governed by only the CDDL
36
or only the GPL Version 2, indicate your decision by adding
37
"[Contributor] elects to include this software in this distribution
38
under the [CDDL or GPL Version 2] license." If you do not indicate a
39
single choice of license, a recipient has the option to distribute
40
your version of this file under either the CDDL, the GPL Version 2 or
41
to extend the choice of license to its licensees as provided above.
42
However, if you add GPL Version 2 code and therefore, elected the GPL
43
Version 2 license, then the option applies only if the new code is
44
made subject to such option by the copyright holder.
45
-->
46
<project name="libs.asm" default="netbeans" basedir=".">
47
    <import file="../nbbuild/templates/projectized.xml"/>
48
</project>
(-)a/libs.asm/external/asm-all-5.0.1-license.txt (+37 lines)
Line 0 Link Here
1
Name: OW2 ASM
2
Version: 5.0.1
3
License: INRIA license
4
Origin: http://forge.ow2.org/project/download.php?group_id=23&file_id=19789
5
OSR: 13078
6
Description: Bytecode manipulation library
7
8
*******************************************************************************
9
* ASM: a very small and fast Java bytecode manipulation framework
10
* Copyright (c) 2000-2011 INRIA, France Telecom
11
* All rights reserved.
12
*
13
* Redistribution and use in source and binary forms, with or without
14
* modification, are permitted provided that the following conditions
15
* are met:
16
* 1. Redistributions of source code must retain the above copyright
17
*    notice, this list of conditions and the following disclaimer.
18
* 2. Redistributions in binary form must reproduce the above copyright
19
*    notice, this list of conditions and the following disclaimer in the
20
*    documentation and/or other materials provided with the distribution.
21
* 3. Neither the name of the copyright holders nor the names of its
22
*    contributors may be used to endorse or promote products derived from
23
*    this software without specific prior written permission.
24
*
25
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
35
* THE POSSIBILITY OF SUCH DAMAGE.
36
*******************************************************************************
37
(-)a/libs.asm/external/binaries-list (+1 lines)
Line 0 Link Here
1
2F7553F50B0D14ED811B849C282DA8C1FFC32AAE asm-all-5.0.1.jar
(-)a/libs.asm/manifest.mf (+4 lines)
Line 0 Link Here
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.netbeans.libs.asm
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/asm/Bundle.properties
4
OpenIDE-Module-Specification-Version: 5.1
(-)a/libs.asm/nbproject/project.properties (+48 lines)
Line 0 Link Here
1
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2
#
3
# Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
4
#
5
# Oracle and Java are registered trademarks of Oracle and/or its affiliates.
6
# Other names may be trademarks of their respective owners.
7
#
8
# The contents of this file are subject to the terms of either the GNU
9
# General Public License Version 2 only ("GPL") or the Common
10
# Development and Distribution License("CDDL") (collectively, the
11
# "License"). You may not use this file except in compliance with the
12
# License. You can obtain a copy of the License at
13
# http://www.netbeans.org/cddl-gplv2.html
14
# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
15
# specific language governing permissions and limitations under the
16
# License.  When distributing the software, include this License Header
17
# Notice in each file and include the License file at
18
# nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
19
# particular file as subject to the "Classpath" exception as provided
20
# by Oracle in the GPL Version 2 section of the License file that
21
# accompanied this code. If applicable, add the following below the
22
# License Header, with the fields enclosed by brackets [] replaced by
23
# your own identifying information:
24
# "Portions Copyrighted [year] [name of copyright owner]"
25
#
26
# Contributor(s):
27
#
28
# The Original Software is NetBeans. The Initial Developer of the Original
29
# Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
30
# Microsystems, Inc. All Rights Reserved.
31
#
32
# If you wish your version of this file to be governed by only the CDDL
33
# or only the GPL Version 2, indicate your decision by adding
34
# "[Contributor] elects to include this software in this distribution
35
# under the [CDDL or GPL Version 2] license." If you do not indicate a
36
# single choice of license, a recipient has the option to distribute
37
# your version of this file under either the CDDL, the GPL Version 2 or
38
# to extend the choice of license to its licensees as provided above.
39
# However, if you add GPL Version 2 code and therefore, elected the GPL
40
# Version 2 license, then the option applies only if the new code is
41
# made subject to such option by the copyright holder.
42
43
javac.compilerargs=-Xlint -Xlint:-serial
44
javac.source=1.7
45
module.jar.dir=lib
46
47
release.external/asm-all-5.0.1.jar=lib/ext/asm-5.0.1/asm-all-5.0.1.jar
48
license.file=../external/asm-all-5.0.1-license.txt
(-)a/libs.asm/nbproject/project.xml (+73 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!--
3
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
5
Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
6
7
Oracle and Java are registered trademarks of Oracle and/or its affiliates.
8
Other names may be trademarks of their respective owners.
9
10
11
The contents of this file are subject to the terms of either the GNU
12
General Public License Version 2 only ("GPL") or the Common
13
Development and Distribution License("CDDL") (collectively, the
14
"License"). You may not use this file except in compliance with the
15
License. You can obtain a copy of the License at
16
http://www.netbeans.org/cddl-gplv2.html
17
or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
18
specific language governing permissions and limitations under the
19
License.  When distributing the software, include this License Header
20
Notice in each file and include the License file at
21
nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
22
particular file as subject to the "Classpath" exception as provided
23
by Oracle in the GPL Version 2 section of the License file that
24
accompanied this code. If applicable, add the following below the
25
License Header, with the fields enclosed by brackets [] replaced by
26
your own identifying information:
27
"Portions Copyrighted [year] [name of copyright owner]"
28
29
Contributor(s):
30
31
The Original Software is NetBeans. The Initial Developer of the Original
32
Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
33
Microsystems, Inc. All Rights Reserved.
34
35
If you wish your version of this file to be governed by only the CDDL
36
or only the GPL Version 2, indicate your decision by adding
37
"[Contributor] elects to include this software in this distribution
38
under the [CDDL or GPL Version 2] license." If you do not indicate a
39
single choice of license, a recipient has the option to distribute
40
your version of this file under either the CDDL, the GPL Version 2 or
41
to extend the choice of license to its licensees as provided above.
42
However, if you add GPL Version 2 code and therefore, elected the GPL
43
Version 2 license, then the option applies only if the new code is
44
made subject to such option by the copyright holder.
45
-->
46
<project xmlns="http://www.netbeans.org/ns/project/1">
47
    <type>org.netbeans.modules.apisupport.project</type>
48
    <configuration>
49
        <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
50
            <code-name-base>org.netbeans.libs.asm</code-name-base>
51
            <module-dependencies/>
52
            <public-packages>
53
                <subpackages>org</subpackages>
54
            </public-packages>
55
            <class-path-extension>
56
                <runtime-relative-path>ext/asm-5.0.1/asm-all-5.0.1.jar</runtime-relative-path>
57
                <binary-origin>external/asm-all-5.0.1.jar</binary-origin>
58
            </class-path-extension>
59
<!--            <class-path-extension>
60
                <runtime-relative-path>ext/asm-5.0.1/asm-5.0.1.jar</runtime-relative-path>
61
                <binary-origin>external/asm-5.0.1.jar</binary-origin>
62
            </class-path-extension>
63
            <class-path-extension>
64
                <runtime-relative-path>ext/asm-5.0.1/asm-commons-5.0.1.jar</runtime-relative-path>
65
                <binary-origin>external/asm-commons-5.0.1.jar</binary-origin>
66
            </class-path-extension>
67
            <class-path-extension>
68
                <runtime-relative-path>ext/asm-5.0.1/asm-util-5.0.1.jar</runtime-relative-path>
69
                <binary-origin>external/asm-util-5.0.1.jar</binary-origin>
70
            </class-path-extension>-->
71
        </data>
72
    </configuration>
73
</project>
(-)a/libs.asm/src/org/netbeans/libs/asm/Bundle.properties (+47 lines)
Line 0 Link Here
1
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2
#
3
# Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
4
#
5
# Oracle and Java are registered trademarks of Oracle and/or its affiliates.
6
# Other names may be trademarks of their respective owners.
7
#
8
# The contents of this file are subject to the terms of either the GNU
9
# General Public License Version 2 only ("GPL") or the Common
10
# Development and Distribution License("CDDL") (collectively, the
11
# "License"). You may not use this file except in compliance with the
12
# License. You can obtain a copy of the License at
13
# http://www.netbeans.org/cddl-gplv2.html
14
# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
15
# specific language governing permissions and limitations under the
16
# License.  When distributing the software, include this License Header
17
# Notice in each file and include the License file at
18
# nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
19
# particular file as subject to the "Classpath" exception as provided
20
# by Oracle in the GPL Version 2 section of the License file that
21
# accompanied this code. If applicable, add the following below the
22
# License Header, with the fields enclosed by brackets [] replaced by
23
# your own identifying information:
24
# "Portions Copyrighted [year] [name of copyright owner]"
25
#
26
# Contributor(s):
27
#
28
# The Original Software is NetBeans. The Initial Developer of the Original
29
# Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
30
# Microsystems, Inc. All Rights Reserved.
31
#
32
# If you wish your version of this file to be governed by only the CDDL
33
# or only the GPL Version 2, indicate your decision by adding
34
# "[Contributor] elects to include this software in this distribution
35
# under the [CDDL or GPL Version 2] license." If you do not indicate a
36
# single choice of license, a recipient has the option to distribute
37
# your version of this file under either the CDDL, the GPL Version 2 or
38
# to extend the choice of license to its licensees as provided above.
39
# However, if you add GPL Version 2 code and therefore, elected the GPL
40
# Version 2 license, then the option applies only if the new code is
41
# made subject to such option by the copyright holder.
42
43
OpenIDE-Module-Name=OW2 ASM
44
OpenIDE-Module-Display-Category=Libraries
45
OpenIDE-Module-Short-Description=Bundles OW2 ASM (bytecode manipulation)
46
OpenIDE-Module-Long-Description=\
47
    This module bundles OW2 ASM (bytecode manipulation library).
(-)a/nbbuild/cluster.properties (+1 lines)
Lines 166-171 Link Here
166
nb.cluster.bootstrap.depends=
166
nb.cluster.bootstrap.depends=
167
nb.cluster.bootstrap=\
167
nb.cluster.bootstrap=\
168
        core.startup,\
168
        core.startup,\
169
        libs.asm,\
169
        o.n.bootstrap,\
170
        o.n.bootstrap,\
170
        openide.filesystems,\
171
        openide.filesystems,\
171
        openide.modules,\
172
        openide.modules,\
(-)a/o.n.bootstrap/apichanges.xml (+17 lines)
Lines 53-58 Link Here
53
  </apidefs>
53
  </apidefs>
54
54
55
  <changes>
55
  <changes>
56
    <change id="module.fragments">
57
         <api name="launcher"/>
58
         <summary>Ability to join other module</summary>
59
         <version major="2" minor="69"/>
60
         <date day="11" month="4" year="2014"/>
61
         <author login="sdedic"/>
62
         <compatibility addition="yes" binary="compatible" semantic="compatible" />
63
         <description>
64
         <p>
65
             Module can be declared as <a href="@TOP@architecture-summary.html#modfrag">
66
                 Module Fragment
67
             </a> to join other module loader. Fragment classes may ask to be bytecode-patched
68
             as superclasses of API classes to preserve compatibility.
69
         </p>
70
         </description>
71
         <issue number="243561"/>
72
     </change>
56
     <change id="patch.classes">
73
     <change id="patch.classes">
57
        <api name="launcher"/>
74
        <api name="launcher"/>
58
        <summary>Ability to patch classes</summary>
75
        <summary>Ability to patch classes</summary>
(-)a/o.n.bootstrap/arch.xml (-1 / +24 lines)
Lines 1533-1542 Link Here
1533
        have access to the class instance that is being defined yet.
1533
        have access to the class instance that is being defined yet.
1534
        </p>
1534
        </p>
1535
     </usecase>
1535
     </usecase>
1536
     
1537
     <usecase id="modfrag" name="Module Fragments">
1538
         <p>
1539
            This is a specific case of <a href="#usecase-patch">Patch Classes</a> use-case.
1540
            To maintain binary compatibility when <b>removing</b> methods from API classes,
1541
            the "removed" implementation is actually moved to a special class, which becomes
1542
            a <i>superclass</i> of the original API class. Such special classes should be
1543
            separated into a 'compat' module, which is only loaded in presence of old clients.
1544
         </p>
1545
         <p>
1546
            The compat module should declare it is a fragment of the original API module in its
1547
            <code>MANIFEST.MF</code>
1548
         </p>
1549
            <pre>
1550
OpenIDE-Module-Fragment-Host: orignal.module.codename
1551
            </pre>
1552
        <p>
1553
            which ensures contents of the compat module will be loaded using classloader
1554
            of the 'fragment host' module. The compat module will not get its own classloader.
1555
            The special class itself should be marked using <a href="@TOP@/org/netbeans/modules/PatchPublic.html">PatchPublic</a> annotation,
1556
            which causes it will be patched as superclass and inserted into the inheritance
1557
            chain.
1558
         </p>
1559
     </usecase>
1536
 </answer>
1560
 </answer>
1537
1561
1538
1562
1539
1540
<!--
1563
<!--
1541
        <question id="arch-where" when="init">
1564
        <question id="arch-where" when="init">
1542
            Where one can find sources for your module?
1565
            Where one can find sources for your module?
(-)a/o.n.bootstrap/manifest.mf (-1 / +1 lines)
Lines 1-6 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.netbeans.bootstrap/1
2
OpenIDE-Module: org.netbeans.bootstrap/1
3
OpenIDE-Module-Specification-Version: 2.68
3
OpenIDE-Module-Specification-Version: 2.69
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/Bundle.properties
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/Bundle.properties
5
OpenIDE-Module-Recommends: org.netbeans.NetigsoFramework
5
OpenIDE-Module-Recommends: org.netbeans.NetigsoFramework
6
6
(-)a/o.n.bootstrap/nbproject/project.properties (-1 / +1 lines)
Lines 41-47 Link Here
41
# made subject to such option by the copyright holder.
41
# made subject to such option by the copyright holder.
42
42
43
javac.compilerargs=-Xlint -Xlint:-serial
43
javac.compilerargs=-Xlint -Xlint:-serial
44
javac.source=1.6
44
javac.source=1.7
45
module.jar.dir=lib
45
module.jar.dir=lib
46
module.jar.basename=boot.jar
46
module.jar.basename=boot.jar
47
release.launcher/unix/nbexec=lib/nbexec
47
release.launcher/unix/nbexec=lib/nbexec
(-)a/o.n.bootstrap/nbproject/project.xml (-1 / +9 lines)
Lines 50-60 Link Here
50
            <code-name-base>org.netbeans.bootstrap</code-name-base>
50
            <code-name-base>org.netbeans.bootstrap</code-name-base>
51
            <module-dependencies>
51
            <module-dependencies>
52
                <dependency>
52
                <dependency>
53
                    <code-name-base>org.netbeans.libs.asm</code-name-base>
54
                    <build-prerequisite/>
55
                    <compile-dependency/>
56
                    <run-dependency>
57
                        <specification-version>5.0</specification-version>
58
                    </run-dependency>
59
                </dependency>
60
                <dependency>
53
                    <code-name-base>org.openide.modules</code-name-base>
61
                    <code-name-base>org.openide.modules</code-name-base>
54
                    <build-prerequisite/>
62
                    <build-prerequisite/>
55
                    <compile-dependency/>
63
                    <compile-dependency/>
56
                    <run-dependency>
64
                    <run-dependency>
57
                        <specification-version>7.37</specification-version>
65
                        <specification-version>7.44</specification-version>
58
                    </run-dependency>
66
                    </run-dependency>
59
                </dependency>
67
                </dependency>
60
                <dependency>
68
                <dependency>
(-)a/o.n.bootstrap/src/org/netbeans/ChangeFirer.java (-5 / +5 lines)
Lines 45-51 Link Here
45
package org.netbeans;
45
package org.netbeans;
46
46
47
import java.util.logging.Level;
47
import java.util.logging.Level;
48
import org.openide.util.Utilities;
48
import org.openide.util.BaseUtilities;
49
import java.util.*;
49
import java.util.*;
50
50
51
/** Thread which fires changes in the modules.
51
/** Thread which fires changes in the modules.
Lines 144-153 Link Here
144
        public boolean equals(Object o) {
144
        public boolean equals(Object o) {
145
            if (! (o instanceof Change)) return false;
145
            if (! (o instanceof Change)) return false;
146
            Change c = (Change) o;
146
            Change c = (Change) o;
147
            return Utilities.compareObjects(prop, c.prop) &&
147
            return BaseUtilities.compareObjects(prop, c.prop) &&
148
                   Utilities.compareObjects(source, c.source) &&
148
                   BaseUtilities.compareObjects(source, c.source) &&
149
                   Utilities.compareObjects(old, c.old) &&
149
                   BaseUtilities.compareObjects(old, c.old) &&
150
                   Utilities.compareObjects(nue, c.nue);
150
                   BaseUtilities.compareObjects(nue, c.nue);
151
        }
151
        }
152
        public int hashCode() {
152
        public int hashCode() {
153
            return source.hashCode() ^ (prop == null ? 0 : prop.hashCode());
153
            return source.hashCode() ^ (prop == null ? 0 : prop.hashCode());
(-)a/o.n.bootstrap/src/org/netbeans/JarClassLoader.java (-16 / +31 lines)
Lines 90-96 Link Here
90
import java.util.logging.Logger;
90
import java.util.logging.Logger;
91
import java.util.zip.ZipEntry;
91
import java.util.zip.ZipEntry;
92
import java.util.zip.ZipException;
92
import java.util.zip.ZipException;
93
import org.openide.util.Utilities;
93
import org.openide.util.BaseUtilities;
94
94
95
/**
95
/**
96
 * A ProxyClassLoader capable of loading classes from a set of jar files
96
 * A ProxyClassLoader capable of loading classes from a set of jar files
Lines 176-182 Link Here
176
    }
176
    }
177
177
178
    final void addURL(URL location) throws IOException, URISyntaxException {
178
    final void addURL(URL location) throws IOException, URISyntaxException {
179
        File f = Utilities.toFile(location.toURI());
179
        File f = BaseUtilities.toFile(location.toURI());
180
        assert f.exists() : "URL must be existing local file: " + location;
180
        assert f.exists() : "URL must be existing local file: " + location;
181
181
182
        List<Source> arr = new ArrayList<Source>(Arrays.asList(sources));
182
        List<Source> arr = new ArrayList<Source>(Arrays.asList(sources));
Lines 210-216 Link Here
210
            arr[3], arr[4], arr[5], sealBase);
210
            arr[3], arr[4], arr[5], sealBase);
211
    }
211
    }
212
    
212
    
213
    private Boolean patchingBytecode;
213
    /**
214
     * Bytecode patching helper
215
     */
216
    private PatchByteCode patchingBytecode;
217
    
218
    byte[] getClassData(String name) {
219
        String path = name.replace('.', '/').concat(".class"); // NOI18N
220
        for( int i=0; i<sources.length; i++ ) {
221
            final Source src = sources[i];
222
            byte[] data = src.getClassData(path);
223
            if (data != null) {
224
                return data;
225
            }
226
        }
227
        return null;
228
    }
229
    
214
    @Override
230
    @Override
215
    protected Class doLoadClass(String pkgName, String name) {
231
    protected Class doLoadClass(String pkgName, String name) {
216
        String path = name.replace('.', '/').concat(".class"); // NOI18N
232
        String path = name.replace('.', '/').concat(".class"); // NOI18N
Lines 223-240 Link Here
223
239
224
            synchronized (sources) {
240
            synchronized (sources) {
225
                if (patchingBytecode == null) {
241
                if (patchingBytecode == null) {
226
                    patchingBytecode = findResource("META-INF/.bytecodePatched") != null; // NOI18N
242
                    Enumeration<URL> res = findResources("META-INF/.bytecodePatched"); // NOI18N
227
                    if (patchingBytecode) {
243
                    if (res.hasMoreElements()) {
228
                        LOGGER.log(Level.FINE, "Patching bytecode in {0}", this);
244
                        LOGGER.log(Level.FINE, "Patching bytecode in {0}", this);
229
                    }
245
                    }
246
                    patchingBytecode = PatchByteCode.fromStream(res, this);
230
                }
247
                }
231
            }
248
            }
232
            if (patchingBytecode) {
249
            try {
233
                try {
250
                data = patchingBytecode.apply(name, data);
234
                    data = PatchByteCode.patch(data);
251
            } catch (Exception x) {
235
                } catch (Exception x) {
252
                LOGGER.log(Level.INFO, "Could not bytecode-patch " + name, x);
236
                    LOGGER.log(Level.INFO, "Could not bytecode-patch " + name, x);
237
                }
238
            }
253
            }
239
            
254
            
240
            // Note that we assume that if we are defining a class in this package,
255
            // Note that we assume that if we are defining a class in this package,
Lines 479-485 Link Here
479
                    return this;
494
                    return this;
480
                }
495
                }
481
            }
496
            }
482
            return "jar:" + Utilities.toURI(new VFile()) + "!/"; // NOI18N
497
            return "jar:" + BaseUtilities.toURI(new VFile()) + "!/"; // NOI18N
483
        }
498
        }
484
499
485
        @Override
500
        @Override
Lines 823-829 Link Here
823
            String tmp = getURL().toExternalForm();
838
            String tmp = getURL().toExternalForm();
824
            if (tmp.startsWith("jar:file:") && tmp.endsWith("!/")) {
839
            if (tmp.startsWith("jar:file:") && tmp.endsWith("!/")) {
825
                String path = tmp.substring(9, tmp.length() - 2).replace("%20", " ");
840
                String path = tmp.substring(9, tmp.length() - 2).replace("%20", " ");
826
                if (Utilities.isWindows()) {
841
                if (BaseUtilities.isWindows()) {
827
                    if (path.startsWith("/")) { // NOI18N
842
                    if (path.startsWith("/")) { // NOI18N
828
                        path = path.substring(1);
843
                        path = path.substring(1);
829
                    }
844
                    }
Lines 839-845 Link Here
839
        File dir;
854
        File dir;
840
        
855
        
841
        DirSource(File file) throws MalformedURLException {
856
        DirSource(File file) throws MalformedURLException {
842
            super(Utilities.toURI(file).toURL());
857
            super(BaseUtilities.toURI(file).toURL());
843
            dir = file;
858
            dir = file;
844
        }
859
        }
845
        
860
        
Lines 849-855 Link Here
849
864
850
        protected URL doGetResource(String name) throws MalformedURLException {
865
        protected URL doGetResource(String name) throws MalformedURLException {
851
            File resFile = new File(dir, name);
866
            File resFile = new File(dir, name);
852
            return resFile.exists() ? Utilities.toURI(resFile).toURL() : null;
867
            return resFile.exists() ? BaseUtilities.toURI(resFile).toURL() : null;
853
        }
868
        }
854
        
869
        
855
        protected byte[] readClass(String path) throws IOException {
870
        protected byte[] readClass(String path) throws IOException {
Lines 972-978 Link Here
972
            AGAIN: for (;;) try {
987
            AGAIN: for (;;) try {
973
                final URI uri = new URI(filePath);
988
                final URI uri = new URI(filePath);
974
                if (uri.getScheme().equals("file")) {
989
                if (uri.getScheme().equals("file")) {
975
                    jar = Utilities.toFile(uri).getPath();
990
                    jar = BaseUtilities.toFile(uri).getPath();
976
                } else {
991
                } else {
977
                    jar = null;
992
                    jar = null;
978
                }
993
                }
(-)a/o.n.bootstrap/src/org/netbeans/LocaleVariants.java (-2 / +2 lines)
Lines 51-57 Link Here
51
import java.util.logging.Logger;
51
import java.util.logging.Logger;
52
import org.openide.modules.InstalledFileLocator;
52
import org.openide.modules.InstalledFileLocator;
53
import org.openide.util.NbBundle;
53
import org.openide.util.NbBundle;
54
import org.openide.util.Utilities;
54
import org.openide.util.BaseUtilities;
55
55
56
final class LocaleVariants implements Stamps.Updater {
56
final class LocaleVariants implements Stamps.Updater {
57
    private static final Logger err = Util.err;
57
    private static final Logger err = Util.err;
Lines 237-243 Link Here
237
    static synchronized String[] getLocalizingSuffixesFast() {
237
    static synchronized String[] getLocalizingSuffixesFast() {
238
        if (suffixes == null ||
238
        if (suffixes == null ||
239
                Locale.getDefault() != lastLocale ||
239
                Locale.getDefault() != lastLocale ||
240
                !Utilities.compareObjects(NbBundle.getBranding(), lastBranding)) {
240
                !BaseUtilities.compareObjects(NbBundle.getBranding(), lastBranding)) {
241
            List<String> _suffixes = new ArrayList<String>();
241
            List<String> _suffixes = new ArrayList<String>();
242
            Iterator<String> it = NbBundle.getLocalizingSuffixes();
242
            Iterator<String> it = NbBundle.getLocalizingSuffixes();
243
            while (it.hasNext()) {
243
            while (it.hasNext()) {
(-)a/o.n.bootstrap/src/org/netbeans/Module.java (+4 lines)
Lines 264-269 Link Here
264
        return data().getCodeName();
264
        return data().getCodeName();
265
    }
265
    }
266
    
266
    
267
    String getFragmentHostCodeName() {
268
        return data().getFragmentHostCodeName();
269
    }
270
    
267
    @Override
271
    @Override
268
    public String getCodeNameBase() {
272
    public String getCodeNameBase() {
269
        String cnb = mgr.cnbFor(getJarFile());
273
        String cnb = mgr.cnbFor(getJarFile());
(-)a/o.n.bootstrap/src/org/netbeans/ModuleData.java (-1 / +30 lines)
Lines 61-66 Link Here
61
import java.util.logging.Logger;
61
import java.util.logging.Logger;
62
import org.netbeans.Module.PackageExport;
62
import org.netbeans.Module.PackageExport;
63
import org.openide.modules.Dependency;
63
import org.openide.modules.Dependency;
64
import org.openide.modules.PatchFor;
64
import org.openide.modules.SpecificationVersion;
65
import org.openide.modules.SpecificationVersion;
65
import org.openide.util.Exceptions;
66
import org.openide.util.Exceptions;
66
import org.openide.util.NbBundle;
67
import org.openide.util.NbBundle;
Lines 85-91 Link Here
85
    private final Dependency[] dependencies;
86
    private final Dependency[] dependencies;
86
    private final Set<String> coveredPackages;
87
    private final Set<String> coveredPackages;
87
    private final String agentClass;
88
    private final String agentClass;
88
    
89
    private final String fragmentHostCodeName;
89
    
90
    
90
    ModuleData(Manifest mf, Module forModule) throws InvalidException {
91
    ModuleData(Manifest mf, Module forModule) throws InvalidException {
91
        Attributes attr = mf.getMainAttributes();
92
        Attributes attr = mf.getMainAttributes();
Lines 211-216 Link Here
211
                this.friendNames = set;
212
                this.friendNames = set;
212
            }
213
            }
213
            this.dependencies = initDeps(forModule, deps, attr);
214
            this.dependencies = initDeps(forModule, deps, attr);
215
            String classLoader = attr.getValue(PatchFor.MANIFEST_FRAGMENT_HOST); // NOI18N
216
            if (classLoader != null) {
217
                Object[] clParse = Util.parseCodeName(classLoader);
218
                String frag = (String)clParse[0];
219
                if (frag != null) {
220
                    if ((frag = frag.trim()).isEmpty()) {
221
                        frag = null;
222
                    }
223
                }
224
                this.fragmentHostCodeName = frag;
225
                if (verifyCNBs && frag != null) {
226
                    // Indirect way of checking syntax:
227
                    Dependency.create(Dependency.TYPE_MODULE, fragmentHostCodeName);
228
                }
229
            } else {
230
                fragmentHostCodeName = null;
231
            }
214
        } catch (IllegalArgumentException iae) {
232
        } catch (IllegalArgumentException iae) {
215
            throw (InvalidException) new InvalidException("While parsing " + codeName + " a dependency attribute: " + iae.toString()).initCause(iae); // NOI18N
233
            throw (InvalidException) new InvalidException("While parsing " + codeName + " a dependency attribute: " + iae.toString()).initCause(iae); // NOI18N
216
        }
234
        }
Lines 249-254 Link Here
249
        this.dependencies = computeImported(mf.getMainAttributes());
267
        this.dependencies = computeImported(mf.getMainAttributes());
250
        this.coveredPackages = new HashSet<String>();
268
        this.coveredPackages = new HashSet<String>();
251
        this.agentClass = getMainAttribute(mf, "Agent-Class"); // NOI18N
269
        this.agentClass = getMainAttribute(mf, "Agent-Class"); // NOI18N
270
        this.fragmentHostCodeName = null;
252
    }
271
    }
253
    
272
    
254
    ModuleData(ObjectInput dis) throws IOException {
273
    ModuleData(ObjectInput dis) throws IOException {
Lines 265-270 Link Here
265
            this.specVers = new SpecificationVersion(dis.readUTF());
284
            this.specVers = new SpecificationVersion(dis.readUTF());
266
            this.publicPackages = Module.PackageExport.read(dis);
285
            this.publicPackages = Module.PackageExport.read(dis);
267
            this.agentClass = dis.readUTF();
286
            this.agentClass = dis.readUTF();
287
            String s = dis.readUTF();
288
            if (s != null) {
289
                s = s.trim();
290
            }
291
            this.fragmentHostCodeName = s == null || s.isEmpty() ? null : s;
268
        } catch (ClassNotFoundException cnfe) {
292
        } catch (ClassNotFoundException cnfe) {
269
            throw new IOException(cnfe);
293
            throw new IOException(cnfe);
270
        }
294
        }
Lines 283-288 Link Here
283
        dos.writeUTF(specVers != null ? specVers.toString() : "0");
307
        dos.writeUTF(specVers != null ? specVers.toString() : "0");
284
        Module.PackageExport.write(dos, publicPackages);
308
        Module.PackageExport.write(dos, publicPackages);
285
        dos.writeUTF(agentClass == null ? "" : agentClass);
309
        dos.writeUTF(agentClass == null ? "" : agentClass);
310
        dos.writeUTF(fragmentHostCodeName == null ? "" : fragmentHostCodeName);
286
    }
311
    }
287
312
288
    private Dependency[] computeImported(Attributes attr) {
313
    private Dependency[] computeImported(Attributes attr) {
Lines 428-433 Link Here
428
        forModule.refineDependencies(deps);
453
        forModule.refineDependencies(deps);
429
        return deps.toArray(new Dependency[deps.size()]);
454
        return deps.toArray(new Dependency[deps.size()]);
430
    }
455
    }
456
    
457
    final String getFragmentHostCodeName() {
458
        return fragmentHostCodeName;
459
    }
431
460
432
    final String getCodeName() {
461
    final String getCodeName() {
433
        return codeName;
462
        return codeName;
(-)a/o.n.bootstrap/src/org/netbeans/ModuleManager.java (-1 / +95 lines)
Lines 116-121 Link Here
116
    // the same, indexed by code name base
116
    // the same, indexed by code name base
117
    private final Map<String,Module> modulesByName = new HashMap<String,Module>(100);
117
    private final Map<String,Module> modulesByName = new HashMap<String,Module>(100);
118
118
119
    /**
120
     * Modules whose contents is injected into 
121
     */
122
    private final Map<String, Collection<Module>> fragmentModules = new HashMap<String, Collection<Module>>(5);
123
119
    // for any module, set of known failed dependencies or problems,
124
    // for any module, set of known failed dependencies or problems,
120
    // or null if this has not been computed yet
125
    // or null if this has not been computed yet
121
    private final Object MODULE_PROBLEMS_LOCK = new Object();
126
    private final Object MODULE_PROBLEMS_LOCK = new Object();
Lines 825-834 Link Here
825
        return installer.refineProvides (m);
830
        return installer.refineProvides (m);
826
    }
831
    }
827
    /** Used by Module to communicate with the ModuleInstaller re. classloader. */
832
    /** Used by Module to communicate with the ModuleInstaller re. classloader. */
828
    public void refineClassLoader(Module m, List parents) {
833
    public ClassLoader refineClassLoader(Module m, List parents) {
829
        // #27853:
834
        // #27853:
830
        installer.refineClassLoader(m, parents);
835
        installer.refineClassLoader(m, parents);
836
        // if fragment, integrate into the host's classloader. Should be called under mutex()
837
        String fragmentHost = m.getFragmentHostCodeName();
838
        if (fragmentHost == null) {
839
            return null;
840
        }
841
        Module theHost = modulesByName.get(fragmentHost);
842
        if (theHost == null) {
843
            throw new IllegalStateException("Missing hosting module " + fragmentHost + " for fragment " + m.getCodeName());
844
        }
845
        return theHost.getClassLoader();
831
    }
846
    }
847
    
848
    /**
849
     * Refines the module's own path with patches from other modules
850
     * @param m the module
851
     * @param path the ordered list of classpath fragments
852
     */
853
    void refineModulePath(Module m, List<File> path) {
854
        String cnb = m.getCodeNameBase();
855
        Collection<Module> injectList = fragmentModules.get(cnb);
856
        if (injectList == null) {
857
            return;
858
        }
859
        for (Module inject : injectList) {
860
            Util.err.log(Level.FINER, "Compat: injecting contents of fragment " + inject.getCodeNameBase() + " into " + m.getCodeNameBase());
861
            List<File> allJars = inject.getAllJars();
862
            // PENDING: shouldn't we add those jars first, so they take precedence ?
863
            path.addAll(allJars);
864
        }
865
    }
866
    
832
    /** Use by OneModuleClassLoader to communicate with the ModuleInstaller re. masking. */
867
    /** Use by OneModuleClassLoader to communicate with the ModuleInstaller re. masking. */
833
    public boolean shouldDelegateResource(Module m, Module parent, String pkg) {
868
    public boolean shouldDelegateResource(Module m, Module parent, String pkg) {
834
        // Cf. #19621:
869
        // Cf. #19621:
Lines 903-908 Link Here
903
        modules.add(m);
938
        modules.add(m);
904
        modulesByName.put(m.getCodeNameBase(), m);
939
        modulesByName.put(m.getCodeNameBase(), m);
905
        providersOf.possibleProviderAdded(m);
940
        providersOf.possibleProviderAdded(m);
941
        
906
        lookup.add(m);
942
        lookup.add(m);
907
        firer.created(m);
943
        firer.created(m);
908
        firer.change(new ChangeFirer.Change(this, PROP_MODULES, null, null));
944
        firer.change(new ChangeFirer.Change(this, PROP_MODULES, null, null));
Lines 913-918 Link Here
913
        clearProblemCache();
949
        clearProblemCache();
914
        firer.fire();
950
        firer.fire();
915
    }
951
    }
952
    
953
    /**
954
     * Attaches a fragment to an existing module. The hosting module must NOT
955
     * be already enabled, otherwise an exception will be thrown. Enabled module
956
     * may have some classes already loaded, and they cannot be patched.
957
     * 
958
     * @param m module to attach if it is a fragment
959
     */
960
    private void attachModuleFragment(Module m) {
961
        String codeNameBase = m.getFragmentHostCodeName();
962
        if (codeNameBase == null) {
963
            return;
964
        }
965
        Module host = modulesByName.get(codeNameBase);
966
        if (host != null && host.isEnabled()) {
967
            throw new IllegalStateException("Module " + host.getCodeName() + " is already enabled");
968
        }
969
        Collection<Module> frags = fragmentModules.get(codeNameBase);
970
        if (frags == null) {
971
            frags = new ArrayList<Module>(1);
972
            fragmentModules.put(codeNameBase, frags);
973
        }
974
        frags.add(m);
975
        
976
    }
977
    
978
    /**
979
     * Removes a fragment module. Throws an exception if the fragment's
980
     * host is already enabled and its classloader may have loaded fragment's
981
     * contents.
982
     * <p/>
983
     * The method does nothing for non-fragment modules
984
     * 
985
     * @param m the module to remove
986
     */
987
    private void removeFragmentFromHost(Module m) {
988
        String fragHost = m.getFragmentHostCodeName();
989
        if (fragHost == null) {
990
            return;
991
        }
992
        Module hostMod = modulesByName.get(fragHost);
993
        if (hostMod != null && hostMod.isEnabled()) {
994
            throw new IllegalStateException("Host module " + m.getCodeName() + " was loaded, cannot remove fragment");
995
        }
996
        Collection<Module> frags = fragmentModules.get(fragHost);
997
        if (frags != null) {
998
            frags.remove(m);
999
        }
1000
    }
916
1001
917
    /** Remove a module from the managed set.
1002
    /** Remove a module from the managed set.
918
     * Must be disabled first.
1003
     * Must be disabled first.
Lines 923-928 Link Here
923
        if (m.isFixed()) throw new IllegalArgumentException("fixed module: " + m); // NOI18N
1008
        if (m.isFixed()) throw new IllegalArgumentException("fixed module: " + m); // NOI18N
924
        if (m.isEnabled()) throw new IllegalArgumentException("enabled module: " + m); // NOI18N
1009
        if (m.isEnabled()) throw new IllegalArgumentException("enabled module: " + m); // NOI18N
925
        ev.log(Events.DELETE_MODULE, m);
1010
        ev.log(Events.DELETE_MODULE, m);
1011
        removeFragmentFromHost(m);
926
        modules.remove(m);
1012
        modules.remove(m);
927
        modulesByName.remove(m.getCodeNameBase());
1013
        modulesByName.remove(m.getCodeNameBase());
928
        providersOf.possibleProviderRemoved(m);
1014
        providersOf.possibleProviderRemoved(m);
Lines 1050-1055 Link Here
1050
        ev.log(Events.START_ENABLE_MODULES, toEnable);
1136
        ev.log(Events.START_ENABLE_MODULES, toEnable);
1051
        netigso.willEnable(toEnable);
1137
        netigso.willEnable(toEnable);
1052
        for (;;) {
1138
        for (;;) {
1139
            // first connect fragments to their hosts, so classloaders are populated
1140
            for (Module m: toEnable) {
1141
                if (m.isEnabled()) {
1142
                    continue;
1143
                }
1144
                // store information from fragment modules for early initialization of hosting classlaoder:
1145
                attachModuleFragment(m);
1146
            }
1053
            // Actually turn on the listed modules.
1147
            // Actually turn on the listed modules.
1054
            // List of modules that need to be "rolled back".
1148
            // List of modules that need to be "rolled back".
1055
            LinkedList<Module> fallback = new LinkedList<Module>();
1149
            LinkedList<Module> fallback = new LinkedList<Module>();
(-)a/o.n.bootstrap/src/org/netbeans/PatchByteCode.java (-2 / +356 lines)
Lines 44-50 Link Here
44
44
45
package org.netbeans;
45
package org.netbeans;
46
46
47
import java.io.IOException;
48
import java.io.InputStream;
49
import java.io.InputStreamReader;
47
import java.io.UnsupportedEncodingException;
50
import java.io.UnsupportedEncodingException;
51
import java.net.URL;
52
import java.util.Collection;
53
import java.util.Enumeration;
54
import java.util.HashMap;
55
import java.util.Iterator;
56
import java.util.Map;
57
import java.util.Properties;
58
import java.util.logging.Logger;
59
import org.objectweb.asm.ClassReader;
60
import org.objectweb.asm.ClassWriter;
61
import org.objectweb.asm.Opcodes;
62
import org.objectweb.asm.signature.SignatureReader;
63
import org.objectweb.asm.signature.SignatureVisitor;
64
import org.objectweb.asm.tree.AbstractInsnNode;
65
import org.objectweb.asm.tree.AnnotationNode;
66
import org.objectweb.asm.tree.ClassNode;
67
import org.objectweb.asm.tree.MethodInsnNode;
68
import org.objectweb.asm.tree.MethodNode;
48
import org.openide.modules.PatchedPublic;
69
import org.openide.modules.PatchedPublic;
49
70
50
/**
71
/**
Lines 54-70 Link Here
54
 * @see #patch
75
 * @see #patch
55
 */
76
 */
56
public final class PatchByteCode {
77
public final class PatchByteCode {
57
78
    private static final Logger LOG = Logger.getLogger(PatchByteCode.class.getName());
79
    
58
    private static final byte[] RUNTIME_INVISIBLE_ANNOTATIONS, PATCHED_PUBLIC;
80
    private static final byte[] RUNTIME_INVISIBLE_ANNOTATIONS, PATCHED_PUBLIC;
81
    private static final String DESC_CTOR_ANNOTATION = "Lorg/openide/modules/ConstructorDelegate;";
82
    private static final String DESC_PATCHED_PUBLIC_ANNOTATION = "Lorg/openide/modules/PatchedPublic;";
83
    private static final String DESC_DEFAULT_CTOR = "()V";
84
    private static final String CONSTRUCTOR_NAME = "<init>"; // NOI18N
85
    private static final String PREFIX_EXTEND = "extend."; // NOI18N
86
    
59
    static {
87
    static {
60
        try {
88
        try {
61
            RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations".getBytes("UTF-8"); // NOI18N
89
            RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations".getBytes("UTF-8"); // NOI18N
62
            PATCHED_PUBLIC = ("L" + PatchedPublic.class.getName().replace('.', '/') + ";").getBytes("UTF-8"); // NOI18N
90
            PATCHED_PUBLIC = DESC_PATCHED_PUBLIC_ANNOTATION.getBytes("UTF-8"); // NOI18N
63
        } catch (UnsupportedEncodingException x) {
91
        } catch (UnsupportedEncodingException x) {
64
            throw new ExceptionInInitializerError(x);
92
            throw new ExceptionInInitializerError(x);
65
        }
93
        }
66
    }
94
    }
95
    
96
    /**
97
     * Shared instance, which does just nothing
98
     */
99
    private static final PatchByteCode NOP = new PatchByteCode(false, null, null);
100
    
101
    /**
102
     * Shared instance, that performs a very fast PatchedPublic patch on the loaded class
103
     */
104
    private static final PatchByteCode PUBLIC_ONLY = new PatchByteCode(true, null, null);
105
    
106
    private final boolean patchPublic;
107
    private final Map<String, String> classToExtend;
108
    private final ClassLoader theClassLoader;
109
    
110
    private PatchByteCode() {
111
        this(false, null, null);
112
    }
113
    
114
    private PatchByteCode(boolean pub, Map<String, String> classToExtend, ClassLoader ldr) {
115
        this.patchPublic = pub;
116
        this.classToExtend = classToExtend;
117
        this.theClassLoader = ldr;
118
    }
119
    
120
    private void load(URL stream) throws IOException {
121
        try (InputStream istm = stream.openStream()) {
122
            Properties props = new Properties();
123
            props.load(new InputStreamReader(istm, "UTF-8")); // NOI18N
124
            
125
            Enumeration<String> en = (Enumeration<String>)props.propertyNames();
126
            
127
            while (en.hasMoreElements()) {
128
                String pn = en.nextElement();
129
                if (pn.startsWith(PREFIX_EXTEND)) {
130
                    String toExtend = pn.substring(PREFIX_EXTEND.length());
131
                    String extendWith = props.getProperty(pn);
132
                    
133
                    String old;
134
                    
135
                    if ((old = classToExtend.put(toExtend, extendWith)) != null) {
136
                        throw new IOException("Multiple extend instructions for class" + toExtend + ": " + extendWith + " and " + old);
137
                    }
138
                }
139
            }
140
        }
141
    }
142
    
143
    private PatchByteCode purify() {
144
        if (classToExtend == null || classToExtend.isEmpty()) {
145
            return PUBLIC_ONLY;
146
        } else {
147
            return this;
148
        }
149
    }
150
    
151
    static PatchByteCode fromStream(Enumeration<URL> streams, ClassLoader ldr) {
152
        PatchByteCode pb = new PatchByteCode(false, new HashMap<String, String>(3), ldr);
153
        boolean found = false;
154
        while (streams.hasMoreElements()) {
155
            URL stream = streams.nextElement();
156
            try {
157
                pb.load(stream);
158
            } catch (IOException ex) {
159
                // TODO: log
160
            }
161
            found = true;
162
        }
163
        
164
        return found ? pb.purify() : NOP;
165
    }
67
166
167
    byte[] apply(String className, byte[] data) throws IOException {
168
        if (patchPublic) {
169
            return patch(data);
170
        } else if (classToExtend == null) {
171
            return data;
172
        }
173
        // more thorough analysis is needed.
174
        String extender = classToExtend.get(className);
175
        if (extender == null) {
176
            return patch(data);
177
        }
178
        // must analyze the extender class, as some annotations there may trigger 
179
        ClassReader clr = new ClassReader(data);
180
        ClassWriter wr = new ClassWriter(clr, 0);
181
        ClassNode theClass = new ClassNode();
182
        
183
        clr.accept(theClass, 0);
184
        
185
        MethodNode defCtor = null;
186
        
187
        String extInternalName = extender.replace(".", "/"); // NOI18N
188
        
189
        // patch the superclass
190
        theClass.superName = extInternalName;
191
        String resName = extInternalName + ".class"; // NOI18N
192
        
193
        try (InputStream istm = theClassLoader.getResourceAsStream(resName)) {
194
            if (istm == null) {
195
                throw new IOException("Could not find classfile for extender class"); // NOI18N
196
            }
197
            ClassReader extenderReader = new ClassReader(istm);
198
            ClassNode extenderClass = new ClassNode();
199
            extenderReader.accept(extenderClass, ClassReader.SKIP_FRAMES);
200
            
201
            // search for a no-arg ctor, replace all invokespecial calls in ctors
202
            for (MethodNode m : (Collection<MethodNode>)theClass.methods) {
203
                if (CONSTRUCTOR_NAME.equals(m.name)) {
204
                    if (DESC_DEFAULT_CTOR.equals(m.desc)) { // NOI18N
205
                        defCtor = m;
206
                    }
207
                    replaceSuperCtorCalls(theClass, extenderClass, m);
208
                }
209
            }
210
            for (Object o : extenderClass.methods) {
211
                MethodNode mn = (MethodNode)o;
212
213
                if (mn.invisibleAnnotations != null && (mn.access & Opcodes.ACC_STATIC) > 0) {
214
                    // constructor, possibly annotated
215
                    for (AnnotationNode an : (Collection<AnnotationNode>)mn.invisibleAnnotations) {
216
                        if (DESC_CTOR_ANNOTATION.equals(an.desc)) {
217
                            delegateToFactory(extenderClass, mn, theClass, defCtor);
218
                            break;
219
                        }
220
                    }
221
                }
222
            }
223
            
224
            for (MethodNode mn : (Collection<MethodNode>)theClass.methods) {
225
                if (mn.invisibleAnnotations == null) {
226
                    continue;
227
                }
228
                for (AnnotationNode an : (Collection<AnnotationNode>)mn.invisibleAnnotations) {
229
                    if (DESC_PATCHED_PUBLIC_ANNOTATION.equals(an.desc)) {
230
                        mn.access = (mn.access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) | Opcodes.ACC_PUBLIC;
231
                        break;
232
                    }
233
                }
234
            }
235
        }
236
        
237
        theClass.accept(wr);
238
        byte[] result = wr.toByteArray();
239
        return result;
240
    }
241
242
    /**
243
     * Replaces class references in super constructor invocations.
244
     * Must not replace references in this() constructor invocations.
245
     * 
246
     * @param theClass the class being patched
247
     * @param extenderClass the injected superclass
248
     * @param mn method to process
249
     */
250
    private void replaceSuperCtorCalls(final ClassNode theClass, final ClassNode extenderClass, MethodNode mn) {
251
        for (Iterator it = mn.instructions.iterator(); it.hasNext(); ) {
252
            AbstractInsnNode aIns = (AbstractInsnNode)it.next();
253
            if (aIns.getOpcode() == Opcodes.INVOKESPECIAL) {
254
                MethodInsnNode mins = (MethodInsnNode)aIns;
255
                if (CONSTRUCTOR_NAME.equals(mins.name) && !mins.owner.equals(theClass.name)) {
256
                    // replace with the extender class name
257
                    mins.owner = extenderClass.name;
258
                }
259
                break;
260
            }
261
        }
262
    }
263
    
264
    /**
265
     * No-op singature visitor
266
     */
267
    private static class NullSignVisitor extends SignatureVisitor {
268
        public NullSignVisitor() {
269
            super(Opcodes.ASM5);
270
        }
271
    }
272
    
273
    /**
274
     * Pushes parameters with correct opcodes that correspond to the
275
     * method's signature. Assumes that the first parameter is the
276
     * object's class itself.
277
     */
278
    private static class CallParametersWriter extends SignatureVisitor {
279
        private final MethodNode mn;
280
        private int localSize;
281
        
282
        /**
283
         * Adds opcodes to the method's code
284
         * 
285
         * @param mn method to generate
286
         * @param firstSelf if true, assumes the first parameter is reference to self and will generate aload_0
287
         */
288
        public CallParametersWriter(MethodNode mn, boolean firstSelf) {
289
            super(Opcodes.ASM5);
290
            this.mn = mn;
291
            this.paramIndex = firstSelf ? 0 : 1;
292
        }
293
        
294
        private int paramIndex = 1;
295
296
        @Override
297
        public void visitEnd() {
298
            // end of classtype
299
            mn.visitVarInsn(Opcodes.ALOAD, paramIndex++);
300
            localSize++;
301
        }
302
303
        @Override
304
        public void visitBaseType(char c) {
305
            int idx = paramIndex++;
306
            int opcode;
307
308
            switch (c) {
309
                // two-word data
310
                case 'J': opcode = Opcodes.LLOAD; paramIndex++; localSize++; break;
311
                case 'D': opcode = Opcodes.DLOAD; paramIndex++; localSize++; break;
312
                // float has a special opcode
313
                case 'F': opcode = Opcodes.FLOAD; break;
314
                default: opcode = Opcodes.ILOAD; break;
315
316
            }
317
            mn.visitVarInsn(opcode, idx);
318
            localSize++;
319
        }
320
321
        @Override
322
        public SignatureVisitor visitTypeArgument(char c) {
323
            return new NullSignVisitor();
324
        }
325
326
        @Override
327
        public void visitTypeArgument() {}
328
329
        @Override
330
        public void visitInnerClassType(String string) {}
331
332
        @Override
333
        public void visitClassType(String string) {}
334
335
        @Override
336
        public SignatureVisitor visitArrayType() {
337
            mn.visitVarInsn(Opcodes.ALOAD, paramIndex++);
338
            return new NullSignVisitor();
339
        }
340
341
        @Override
342
        public void visitTypeVariable(String string) {}
343
344
        @Override
345
        public SignatureVisitor visitExceptionType() {
346
            return new NullSignVisitor();
347
        }
348
349
        @Override
350
        public SignatureVisitor visitReturnType() {
351
            return new NullSignVisitor();
352
        }
353
354
        @Override
355
        public SignatureVisitor visitParameterType() {
356
            return this;
357
        }
358
359
        @Override
360
        public SignatureVisitor visitInterface() {
361
            return null;
362
        }
363
364
        @Override
365
        public SignatureVisitor visitSuperclass() {
366
            return null;
367
        }
368
369
        @Override
370
        public SignatureVisitor visitInterfaceBound() {
371
            return new NullSignVisitor();
372
        }
373
374
        @Override
375
        public SignatureVisitor visitClassBound() {
376
            return new NullSignVisitor();
377
        }
378
379
        @Override
380
        public void visitFormalTypeParameter(String string) {
381
            super.visitFormalTypeParameter(string); //To change body of generated methods, choose Tools | Templates.
382
        }
383
384
    }
385
386
    private void delegateToFactory(ClassNode targetClass, MethodNode targetMethod, ClassNode clazz,
387
            MethodNode noArgCtor) {
388
        String desc = targetMethod.desc;
389
        // assume the first parameter is the class:
390
        int nextPos = desc.indexOf(';', 2); // NOI18N
391
        desc = "(" + desc.substring(nextPos + 1); // NOI18N
392
        MethodNode mn = new MethodNode(Opcodes.ASM5, 
393
                targetMethod.access & (~Opcodes.ACC_STATIC), CONSTRUCTOR_NAME,
394
                desc,
395
                targetMethod.signature,
396
                (String[])targetMethod.exceptions.toArray(new String[targetMethod.exceptions.size()]));
397
        
398
        mn.visibleAnnotations = targetMethod.visibleAnnotations;
399
        mn.visibleParameterAnnotations = targetMethod.visibleParameterAnnotations;
400
        mn.parameters = targetMethod.parameters;
401
        mn.exceptions = targetMethod.exceptions;
402
        mn.visitCode();
403
        // this();
404
        mn.visitVarInsn(Opcodes.ALOAD, 0);
405
        mn.visitMethodInsn(Opcodes.INVOKESPECIAL, 
406
                clazz.name, 
407
                noArgCtor.name, noArgCtor.desc, false);
408
        
409
        // push parameters
410
        SignatureReader r = new SignatureReader(targetMethod.desc);
411
        CallParametersWriter callWr = new CallParametersWriter(mn, true);
412
        r.accept(callWr);
413
        mn.visitMethodInsn(Opcodes.INVOKESTATIC, targetClass.name, targetMethod.name, targetMethod.desc, false);
414
        
415
        mn.visitInsn(Opcodes.RETURN);
416
        mn.maxStack = callWr.localSize;
417
        mn.maxLocals = callWr.localSize;
418
        
419
        clazz.methods.add(mn);
420
    }
421
    
68
    /**
422
    /**
69
     * Patches a class if it is needed.
423
     * Patches a class if it is needed.
70
     * @param arr the bytecode
424
     * @param arr the bytecode
(-)a/o.n.bootstrap/src/org/netbeans/StandardModule.java (-10 / +17 lines)
Lines 78-84 Link Here
78
import org.openide.modules.InstalledFileLocator;
78
import org.openide.modules.InstalledFileLocator;
79
import org.openide.util.Exceptions;
79
import org.openide.util.Exceptions;
80
import org.openide.util.NbBundle;
80
import org.openide.util.NbBundle;
81
import org.openide.util.Utilities;
81
import org.openide.util.BaseUtilities;
82
82
83
/** Object representing one module, possibly installed.
83
/** Object representing one module, possibly installed.
84
 * Responsible for opening of module JAR file; reading
84
 * Responsible for opening of module JAR file; reading
Lines 542-556 Link Here
542
        }
542
        }
543
        
543
        
544
        ((StandardModuleData)data()).addCp(classp);
544
        ((StandardModuleData)data()).addCp(classp);
545
546
        // possibly inject some patches
547
        getManager().refineModulePath(this, classp);
545
        
548
        
546
        // #27853:
549
        // #27853
547
        getManager().refineClassLoader(this, loaders);
550
        ClassLoader cld = getManager().refineClassLoader(this, loaders);
548
        
551
        // the classloader may be shared, if this module is a fragment
549
        try {
552
        if (cld != null) {
550
            classloader = createNewClassLoader(classp, loaders);
553
            classloader = cld;
551
        } catch (IllegalArgumentException iae) {
554
        } else {
552
            // Should not happen, but just in case.
555
            try {
553
            throw (IOException) new IOException(iae.toString()).initCause(iae);
556
                classloader = createNewClassLoader(classp, loaders);
557
            } catch (IllegalArgumentException iae) {
558
                // Should not happen, but just in case.
559
                throw (IOException) new IOException(iae.toString()).initCause(iae);
560
            }
554
        }
561
        }
555
    }
562
    }
556
    
563
    
Lines 667-673 Link Here
667
                return lib.getAbsolutePath();
674
                return lib.getAbsolutePath();
668
            }
675
            }
669
            
676
            
670
            if( Utilities.isMac() ) {
677
            if( BaseUtilities.isMac() ) {
671
                String jniMapped = mapped.replaceFirst("\\.dylib$",".jnilib");
678
                String jniMapped = mapped.replaceFirst("\\.dylib$",".jnilib");
672
                lib = ifl.locate("modules/lib/" + jniMapped, getCodeNameBase(), false); // NOI18N
679
                lib = ifl.locate("modules/lib/" + jniMapped, getCodeNameBase(), false); // NOI18N
673
                if (lib != null) {
680
                if (lib != null) {
(-)a/o.n.bootstrap/test/unit/src/org/netbeans/PatchByteCodeTest.java (-2 / +69 lines)
Lines 45-53 Link Here
45
import java.io.ByteArrayOutputStream;
45
import java.io.ByteArrayOutputStream;
46
import java.io.IOException;
46
import java.io.IOException;
47
import java.io.InputStream;
47
import java.io.InputStream;
48
import java.lang.reflect.Constructor;
49
import java.lang.reflect.Field;
48
import java.lang.reflect.Member;
50
import java.lang.reflect.Member;
49
import java.lang.reflect.Modifier;
51
import java.lang.reflect.Modifier;
52
import java.net.URL;
53
import java.util.Enumeration;
50
import org.netbeans.junit.NbTestCase;
54
import org.netbeans.junit.NbTestCase;
55
import org.openide.modules.ConstructorDelegate;
56
import org.openide.modules.PatchFor;
51
import org.openide.modules.PatchedPublic;
57
import org.openide.modules.PatchedPublic;
52
58
53
public class PatchByteCodeTest extends NbTestCase {
59
public class PatchByteCodeTest extends NbTestCase {
Lines 65-70 Link Here
65
        @PatchedPublic
71
        @PatchedPublic
66
        private void m2() {}
72
        private void m2() {}
67
    }
73
    }
74
    
75
    public static class Superclazz {
76
        public int val;
77
    }
78
    
79
    public static class CAPI extends Superclazz {
80
        public int otherVal;
81
        
82
        public CAPI() {
83
            otherVal = 1;
84
        }
85
    }
86
    
87
    @PatchFor(CAPI.class)
88
    public static class CompatAPI extends Superclazz {
89
        @ConstructorDelegate
90
        protected static void createAPI(CompatAPI inst, int val2) {
91
            inst.val = val2;
92
        }
93
    }
68
94
69
    public void testPatchingPublic() throws Exception {
95
    public void testPatchingPublic() throws Exception {
70
        Class<?> c = new L().loadClass(C.class.getName());
96
        Class<?> c = new L().loadClass(C.class.getName());
Lines 83-88 Link Here
83
        assertEquals(Modifier.PUBLIC, m.getModifiers() & Modifier.PUBLIC);
109
        assertEquals(Modifier.PUBLIC, m.getModifiers() & Modifier.PUBLIC);
84
        assertEquals(0, m.getModifiers() & Modifier.PRIVATE);
110
        assertEquals(0, m.getModifiers() & Modifier.PRIVATE);
85
    }
111
    }
112
    
113
    public void testPatchSuperclass() throws Exception {
114
        Class<?> c = new L().loadClass(CAPI.class.getName());
115
        assertNotSame(c, CAPI.class);
116
        
117
        Class s = c.getSuperclass();
118
        assertNotSame(s, Object.class);
119
        assertEquals(CompatAPI.class.getName(), s.getName());
120
    }
121
    
122
    public void testGeneratedConstructor() throws Exception {
123
        Class<?> c = new L().loadClass(CAPI.class.getName());
124
        Member m = c.getDeclaredConstructor(int.class);
125
        assertEquals(Modifier.PROTECTED, m.getModifiers() & (Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PRIVATE));
126
    }
127
    
128
    public void testExecutingConstructor() throws Exception {
129
        ClassLoader l = new L();
130
        Class<?> c = l.loadClass(CAPI.class.getName());        
131
        Member m = c.getDeclaredConstructor(int.class);
132
        Constructor ctor = (Constructor)m;
133
        ctor.setAccessible(true);
134
        
135
        Object o = ctor.newInstance(5);
136
        assertSame(c, o.getClass());
137
        assertTrue("Invalid API superclass", Superclazz.class.isInstance(o));
138
        
139
        assertEquals("@ConstructorDelegate method did not execute", 5, ((Superclazz)o).val);
140
        
141
        Field f = o.getClass().getField("otherVal");
142
        Object v = f.get(o);
143
        assertEquals("Patched API constructor did not execute", v, 1);
144
    }
145
    
146
    
86
147
87
    private static class L extends ClassLoader {
148
    private static class L extends ClassLoader {
88
149
Lines 92-98 Link Here
92
153
93
        @Override
154
        @Override
94
        protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
155
        protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
95
            if (name.startsWith(PatchByteCodeTest.class.getName() + "$")) {
156
            if (name.startsWith(PatchByteCodeTest.class.getName() + "$C")) {
96
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
157
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
97
                InputStream in = getResourceAsStream(name.replace('.', '/') + ".class");
158
                InputStream in = getResourceAsStream(name.replace('.', '/') + ".class");
98
                int r;
159
                int r;
Lines 103-109 Link Here
103
                } catch (IOException x) {
164
                } catch (IOException x) {
104
                    throw new ClassNotFoundException(name, x);
165
                    throw new ClassNotFoundException(name, x);
105
                }
166
                }
106
                byte[] data = PatchByteCode.patch(baos.toByteArray());
167
                byte[] data;
168
                try {
169
                    Enumeration<URL> res = getResources("META-INF/.bytecodePatched"); // NOI18N
170
                    data = PatchByteCode.fromStream(res, this).apply(name, baos.toByteArray());
171
                } catch (IOException x) {
172
                    throw new ClassNotFoundException(name, x);
173
                }
107
                Class c = defineClass(name, data, 0, data.length);
174
                Class c = defineClass(name, data, 0, data.length);
108
                if (resolve) {
175
                if (resolve) {
109
                    resolveClass(c);
176
                    resolveClass(c);
(-)a/openide.modules/apichanges.xml (+46 lines)
Lines 50-55 Link Here
50
  	<apidef name="modules">Modules API</apidef>
50
  	<apidef name="modules">Modules API</apidef>
51
  </apidefs>
51
  </apidefs>
52
<changes>
52
<changes>
53
    <change id="PatchSuper">
54
        <api name="modules"/>
55
        <summary>Support for binary compatibility patches</summary>
56
        <version major="7" minor="44"/>
57
        <date day="11" month="4" year="2014"/>
58
        <author login="sdedic"/>
59
        <compatibility addition="yes" binary="compatible" semantic="compatible" source="compatible"/>
60
        <description>
61
            <p>
62
                When removing @deprecated code from API, it is still desirable to retain
63
                backward binary compatibility. Because of JVM method/field/class resolution
64
                mechanism, it is permitted to move the implementation upwards the inheritance
65
                hierarchy. However, the superclass that receives the methods must
66
                not be visible in the sources (otherwise the change would not serve any good) and
67
                specifically, must not be referenced from `extends' clause of the changed class.
68
            </p>
69
            <p>
70
                The compatibility superclass ought to reside in a completely different module to
71
                allow reduce type dependencies and should be only injected into inheritance chain
72
                if and only if some of enabled modules depend on the obsolete version of the API module.
73
            </p>
74
            <p>
75
                Special annotation <a href="@TOP@/org/netbeans/modules/PatchFor.html"><code>@PatchFor</code></a>
76
                allows to require that a class should serve as a <i>superclass</i> of some other class.
77
                See <a href="@TOP@/org/netbeans/modules/PatchFor.html">@PatchFor javadoc</a> for further 
78
                constraints.
79
            </p>
80
            <p>
81
                The constructor cannot be 'just moved', because of it contains instance variable initialization
82
                code. To support removing obsolete constructors, 
83
                <a href="@TOP@/org/netbeans/modules/ConstructorDelegate.html"><code>@ConstructorDelegate</code></a> has been
84
                created to mark moved initialization code. See <a href="@TOP@/org/netbeans/modules/ConstructorDelegate.html">@ConstructorDelegate javadoc</a>
85
                for more details.
86
            </p>
87
            <p>
88
                The module which contains such patches must share the classloader with the patched one,
89
                since classes in those two modules reference each other. A new Manifest entry, <strong>
90
                    <code>OpenIDE-Module-Fragment-Host</code>
91
                </strong>, is defined. It's value must be set to <strong>codename</strong> of the
92
                patched module.
93
            </p>
94
        </description>
95
        <class package="org.netbeans.modules" name="PatchFor"/>
96
        <class package="org.netbeans.modules" name="ConstructorDelegate"/>
97
        <issue number="243561"/>
98
    </change>
53
    <change id="javafx.lib">
99
    <change id="javafx.lib">
54
        <api name="modules"/>
100
        <api name="modules"/>
55
        <summary>JavaFX Library Wrapper</summary>
101
        <summary>JavaFX Library Wrapper</summary>
(-)a/openide.modules/arch.xml (+17 lines)
Lines 82-87 Link Here
82
    everyone who wishes to learn more about the NetBeans runtime container
82
    everyone who wishes to learn more about the NetBeans runtime container
83
    behaviour.
83
    behaviour.
84
  </usecase>
84
  </usecase>
85
  <usecase id="patchfor" name="Runtime compatibility patches">
86
      <p>
87
        To maintain binary compatibility, method implementations may be injected
88
        at runtime, in a form of a superclass in the class' inheritance hierarchy.
89
        Modules compiled against older version of APIs which contains MethodReferences to 
90
        methods removed from the oficial APIs will be then linked according to JVM Resolution
91
        algorithm to a matching method present in the superclass of the referenced type.
92
      </p>
93
      <p>
94
        Annotations are used to instruct the ClassLoader to make transformations to the API
95
          classes. <a href="@TOP@/org/netbeans/modules/PatchFor.html">PatchFor</a> causes the annotated
96
          class to be injected as a superclass of the API class identified by the annotation's value.
97
          <a href="@TOP@/org/netbeans/modules/ConstructorDelegate.html">ConstructorDelegate</a> marks
98
          a method, which is called as constructor implementation in the case that it is necessary
99
          to preserve a constructor for binary compatibility.
100
      </p>
101
  </usecase>
85
</answer>
102
</answer>
86
103
87
104
(-)a/openide.modules/manifest.mf (-1 / +1 lines)
Lines 1-5 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.openide.modules
2
OpenIDE-Module: org.openide.modules
3
OpenIDE-Module-Localizing-Bundle: org/openide/modules/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/openide/modules/Bundle.properties
4
OpenIDE-Module-Specification-Version: 7.43
4
OpenIDE-Module-Specification-Version: 7.44
5
5
(-)a/openide.modules/nbproject/project.properties (-1 / +1 lines)
Lines 41-47 Link Here
41
# made subject to such option by the copyright holder.
41
# made subject to such option by the copyright holder.
42
42
43
javac.compilerargs=-Xlint -Xlint:-serial
43
javac.compilerargs=-Xlint -Xlint:-serial
44
javac.source=1.6
44
javac.source=1.7
45
module.jar.dir=lib
45
module.jar.dir=lib
46
javadoc.main.page=org/openide/modules/doc-files/api.html
46
javadoc.main.page=org/openide/modules/doc-files/api.html
47
javadoc.arch=${basedir}/arch.xml
47
javadoc.arch=${basedir}/arch.xml
(-)a/openide.modules/src/org/netbeans/modules/openide/modules/PatchedPublicProcessor.java (-14 / +140 lines)
Lines 42-51 Link Here
42
42
43
package org.netbeans.modules.openide.modules;
43
package org.netbeans.modules.openide.modules;
44
44
45
import java.io.BufferedWriter;
45
import java.io.IOException;
46
import java.io.IOException;
47
import java.io.OutputStream;
48
import java.io.OutputStreamWriter;
46
import java.util.ArrayList;
49
import java.util.ArrayList;
47
import java.util.Collections;
50
import java.util.Arrays;
51
import java.util.HashMap;
52
import java.util.HashSet;
48
import java.util.List;
53
import java.util.List;
54
import java.util.Map;
49
import java.util.Set;
55
import java.util.Set;
50
import javax.annotation.processing.AbstractProcessor;
56
import javax.annotation.processing.AbstractProcessor;
51
import javax.annotation.processing.ProcessingEnvironment;
57
import javax.annotation.processing.ProcessingEnvironment;
Lines 53-97 Link Here
53
import javax.annotation.processing.RoundEnvironment;
59
import javax.annotation.processing.RoundEnvironment;
54
import javax.annotation.processing.SupportedSourceVersion;
60
import javax.annotation.processing.SupportedSourceVersion;
55
import javax.lang.model.SourceVersion;
61
import javax.lang.model.SourceVersion;
62
import javax.lang.model.element.AnnotationMirror;
63
import javax.lang.model.element.AnnotationValue;
56
import javax.lang.model.element.Element;
64
import javax.lang.model.element.Element;
65
import javax.lang.model.element.ElementKind;
66
import javax.lang.model.element.ExecutableElement;
57
import javax.lang.model.element.Modifier;
67
import javax.lang.model.element.Modifier;
58
import javax.lang.model.element.TypeElement;
68
import javax.lang.model.element.TypeElement;
69
import javax.lang.model.type.DeclaredType;
70
import javax.lang.model.type.TypeMirror;
59
import javax.tools.Diagnostic.Kind;
71
import javax.tools.Diagnostic.Kind;
60
import javax.tools.StandardLocation;
72
import javax.tools.StandardLocation;
73
import org.openide.modules.ConstructorDelegate;
74
import org.openide.modules.PatchFor;
61
import org.openide.modules.PatchedPublic;
75
import org.openide.modules.PatchedPublic;
62
import org.openide.util.lookup.ServiceProvider;
76
import org.openide.util.lookup.ServiceProvider;
63
77
64
@ServiceProvider(service=Processor.class)
78
@ServiceProvider(service=Processor.class)
65
@SupportedSourceVersion(SourceVersion.RELEASE_6)
79
@SupportedSourceVersion(SourceVersion.RELEASE_6)
66
public class PatchedPublicProcessor extends AbstractProcessor {
80
public class PatchedPublicProcessor extends AbstractProcessor {
81
    
82
    private static final Set<String> ANNOTATIONS = new HashSet<String>(
83
        Arrays.asList(
84
            PatchedPublic.class.getCanonicalName(),
85
            PatchFor.class.getCanonicalName(),
86
            ConstructorDelegate.class.getCanonicalName()
87
        )
88
    );
67
89
68
    public @Override Set<String> getSupportedAnnotationTypes() {
90
    public @Override Set<String> getSupportedAnnotationTypes() {
69
        return Collections.singleton(PatchedPublic.class.getCanonicalName());
91
        return ANNOTATIONS;
70
    }
92
    }
71
93
72
    private List<Element> originatingElements;
94
    private List<Element> originatingElements;
73
95
    
96
    /**
97
     * Map keyed by the original API class, values are superclasses to be injected
98
     * into the API class.
99
     */
100
    private Map<String, String>     superclasses = new HashMap<String, String>();
101
    
74
    @Override
102
    @Override
75
    public synchronized void init(ProcessingEnvironment processingEnv) {
103
    public synchronized void init(ProcessingEnvironment processingEnv) {
76
        super.init(processingEnv);
104
        super.init(processingEnv);
77
        originatingElements = new ArrayList<Element>();
105
        originatingElements = new ArrayList<Element>();
78
    }
106
    }
107
    
108
    private void flush(RoundEnvironment roundEnv) {
109
        if (!originatingElements.isEmpty()) {
110
            try (OutputStream os = processingEnv.getFiler().createResource(
111
                    StandardLocation.CLASS_OUTPUT,
112
                    "", "META-INF/.bytecodePatched",
113
                    originatingElements.toArray(new Element[originatingElements.size()])).openOutputStream()) {
114
                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
115
                for (Map.Entry<String, String> exEntry : superclasses.entrySet()) {
116
                    String api = exEntry.getKey();
117
                    String sup = exEntry.getValue();
118
                    
119
                    bw.append("extend.").append(api).append("=").append(sup);
120
                    bw.newLine();
121
                }
122
                bw.flush();
123
            } catch (IOException x) {
124
                processingEnv.getMessager().printMessage(Kind.ERROR, x.getMessage());
125
            }
126
        }
127
    }
128
    
129
    private TypeElement implForElement;
130
    private Element valueElement;
131
    private boolean reported;
132
    
133
    private TypeElement getImplFor() {
134
        implForElement = processingEnv.getElementUtils().getTypeElement(IMPL_FOR_NAME);
135
        if (implForElement == null) {
136
            if (!reported) {
137
                processingEnv.getMessager().printMessage(Kind.ERROR, "Cannot find @ImplementationFor annotation");
138
                reported = true;
139
            }
140
            return null;
141
        }
142
        for (Element e : implForElement.getEnclosedElements()) {
143
            if (e.getKind() == ElementKind.METHOD && e.getSimpleName().contentEquals("value")) {
144
                valueElement = e;
145
                break;
146
            }
147
        }
148
        return implForElement;
149
    }
150
    
151
    private static final String IMPL_FOR_NAME = PatchFor.class.getName();
79
152
80
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
153
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
81
        if (roundEnv.processingOver()) {
154
        if (roundEnv.processingOver()) {
82
            if (!originatingElements.isEmpty()) {
155
            flush(roundEnv);
83
                try {
84
                    processingEnv.getFiler().createResource(
85
                            StandardLocation.CLASS_OUTPUT,
86
                            "", "META-INF/.bytecodePatched",
87
                            originatingElements.toArray(new Element[originatingElements.size()])).
88
                            openOutputStream().close();
89
                } catch (IOException x) {
90
                    processingEnv.getMessager().printMessage(Kind.ERROR, x.getMessage());
91
                }
92
            }
93
            return false;
156
            return false;
94
        }
157
        }
158
        
95
        for (Element e : roundEnv.getElementsAnnotatedWith(PatchedPublic.class)) {
159
        for (Element e : roundEnv.getElementsAnnotatedWith(PatchedPublic.class)) {
96
            if (e.getAnnotationMirrors().size() > 1) {
160
            if (e.getAnnotationMirrors().size() > 1) {
97
                processingEnv.getMessager().printMessage(Kind.ERROR, "Cannot currently mix @PatchedPublic with other annotations", e);
161
                processingEnv.getMessager().printMessage(Kind.ERROR, "Cannot currently mix @PatchedPublic with other annotations", e);
Lines 103-108 Link Here
103
            }
167
            }
104
            originatingElements.add(e);
168
            originatingElements.add(e);
105
        }
169
        }
170
        
171
        CLAZZ: for (Element e : roundEnv.getElementsAnnotatedWith(PatchFor.class)) {
172
            List<? extends AnnotationMirror> mirrors = e.getAnnotationMirrors();
173
            String apiName = null;
174
            TypeElement target = null;
175
            
176
            for (AnnotationMirror m : mirrors) {
177
                Element me = m.getAnnotationType().asElement();
178
                if (me != getImplFor()) {
179
                    continue;
180
                }
181
                AnnotationValue val = m.getElementValues().get(valueElement);
182
                if (!(val.getValue() instanceof DeclaredType)) {
183
                    processingEnv.getMessager().printMessage(Kind.ERROR, "Value for @ImplementationFor must be a valid class", e);
184
                    continue CLAZZ;
185
                }
186
                Element x = ((DeclaredType)val.getValue()).asElement();
187
                if (!(x instanceof TypeElement)) {
188
                    processingEnv.getMessager().printMessage(Kind.ERROR, "Value for @ImplementationFor must be a valid class", e);
189
                    continue CLAZZ;
190
                }
191
                target = (TypeElement)x;
192
                if (target.getKind() != ElementKind.CLASS) {
193
                    processingEnv.getMessager().printMessage(Kind.ERROR, "@ImplementationFor can be only applied on classes", e);
194
                    continue CLAZZ;
195
                }
196
                apiName = processingEnv.getElementUtils().getBinaryName(target).toString();
197
                break;
198
            }
199
            if (target == null) {
200
                throw new IllegalStateException();
201
            }
202
            TypeElement t = (TypeElement)e;
203
            
204
            boolean defaultCtorFound = false;
205
            for (Element el : target.getEnclosedElements()) {
206
                if (el.getKind() != ElementKind.CONSTRUCTOR) {
207
                    continue;
208
                }
209
                if (((ExecutableElement)el).getParameters().isEmpty()) {
210
                    defaultCtorFound = true;
211
                    break;
212
                }
213
            }
214
            if (!defaultCtorFound) {
215
                processingEnv.getMessager().printMessage(Kind.ERROR, "Class " + t.getQualifiedName().toString() + " has no default constructor", e);
216
                continue CLAZZ;
217
            }
218
            String superName = processingEnv.getElementUtils().getBinaryName(t).toString();
219
            
220
            TypeMirror patchSuperClass = t.getSuperclass();
221
            TypeMirror targetSuperClass = target.getSuperclass();
222
            
223
            if (!processingEnv.getTypeUtils().isSameType(patchSuperClass, targetSuperClass)) {
224
                processingEnv.getMessager().printMessage(Kind.ERROR, "API class and the substitue differ in their superclasses", e);
225
                continue CLAZZ;
226
            }
227
            superclasses.put(apiName, superName);
228
            processingEnv.getMessager().printMessage(Kind.NOTE, "Adding injection of " + superName + " as a superclass of API " + apiName);
229
            
230
            originatingElements.add(e);
231
        }
106
        return true;
232
        return true;
107
    }
233
    }
108
234
(-)a/openide.modules/src/org/openide/modules/ConstructorDelegate.java (+69 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2014 Sun Microsystems, Inc.
41
 */
42
43
package org.openide.modules;
44
45
import java.lang.annotation.ElementType;
46
import java.lang.annotation.Retention;
47
import java.lang.annotation.RetentionPolicy;
48
import java.lang.annotation.Target;
49
50
/**
51
 * Marks a code that implements constructor contract for backward compatibility.
52
 * There are two possible uses for this annotation. A static void method can be annotated.
53
 * The method must take the compat implementation type as its first parameter. The rest
54
 * of parameters will be used to generate a constructor in the original API class, 
55
 * with the same thrown exceptions and access modifiers. The generated constructor will first call
56
 * <b>the default constructor</b> of the API class, then delegate the initialization work
57
 * to the static method passing <code>this</code> as the first parameter.
58
 * <p/>
59
 * The annotation has only effect if the method's declaring class was annotated using
60
 * {@link PatchFor}.
61
 * 
62
 * @see PatchFor
63
 * @since 7.44
64
 * @author sdedic
65
 */
66
@Retention(RetentionPolicy.CLASS)
67
@Target({ElementType.METHOD})
68
public @interface ConstructorDelegate {
69
}
(-)a/openide.modules/src/org/openide/modules/PatchFor.java (+82 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2014 Sun Microsystems, Inc.
41
 */
42
43
package org.openide.modules;
44
45
import java.lang.annotation.ElementType;
46
import java.lang.annotation.Retention;
47
import java.lang.annotation.RetentionPolicy;
48
import java.lang.annotation.Target;
49
50
/**
51
 * Marks a class, which provides binary-compatible implementation for a changed API class.
52
 * The marked class will be used as a <b>superclass</b> of the API class identified
53
 * by value of the annotation, so old clients can still use removed members at run time.
54
 * <p/>
55
 * The substitute superclass must extend the same type as the original
56
 * API class, so that API-visible inheritance chain is preserved. It must declare
57
 * all non-private constructors as the original superclass.
58
 * <p/>
59
 * The module that contains {@code PatchFor} classes <b>must be</b> defined as
60
 * module fragment: put
61
 * <code><pre>
62
 * OpenIDE-Module-Fragment-Host: codenamebase
63
 * </pre></code>
64
 * into the Module's manifest. The `codenamebase' must identify the host module
65
 * which contains the class(es) to be patched.
66
 * 
67
 * @since 7.44
68
 * @author sdedic
69
 */
70
@Retention(RetentionPolicy.CLASS)
71
@Target({ElementType.TYPE})
72
public @interface PatchFor {
73
    /**
74
     * The manifest header name.
75
     */
76
    public static final String MANIFEST_FRAGMENT_HOST = "OpenIDE-Module-Fragment-Host";
77
    
78
    /**
79
     * @return Class that should be changed to extend the annotated class
80
     */
81
    public Class<?> value();
82
}

Return to bug 243561