Skip to main content
summaryrefslogtreecommitdiffstats
blob: 3998941fe0ffa5ef975d07aaad1bb29c0f7be370 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/**********************************************************************
 * This file is part of "Object Teams Dynamic Runtime Environment"
 * 
 * Copyright 2009, 2014 Oliver Frank and others.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Please visit http://www.eclipse.org/objectteams for updates and contact.
 * 
 * Contributors:
 *		Oliver Frank - Initial API and implementation
 *		Stephan Herrmann - Initial API and implementation
 **********************************************************************/
package org.eclipse.objectteams.otredyn.bytecode.asm;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.eclipse.objectteams.otredyn.bytecode.AbstractBoundClass;
import org.eclipse.objectteams.otredyn.bytecode.AbstractTeam;
import org.eclipse.objectteams.otredyn.bytecode.IBytecodeProvider;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;

/**
 * This class implements the bytecode parsing for {@link AbstractBoundClass}.
 * It parses the bytecode with ASM.
 * @author Oliver Frank
 */
public abstract class AsmBoundClass extends AbstractTeam {
	
	public static final int ASM_API = Opcodes.ASM6;

	private IBytecodeProvider bytecodeProvider;
	
	/**
	 * just a temporary cache for the bytecode
	 */
	private byte[] bytecode;
	
	/**
	 * ordered lists of qualified callin labels
	 */
	public List<String[]> precedenceses = new ArrayList<String[]>();

	/**
	 * Set of base classes to which the current class or one of its roles as playedBy bindings.
	 * Qualified class names are '.' separated.
	 */
	public Set<String> boundBaseClasses;

	protected AsmBoundClass(String name, String id, IBytecodeProvider bytecodeProvider, ClassLoader loader) {
		super(name, id, loader);
		this.bytecodeProvider = bytecodeProvider;
	}

	/**
	 * Parses the bytecode of a class and uses the set/add... Methods (e.g. addMethod)
	 * of {@link AbstractBoundClass} to set the information
	 */
	@Override
	public synchronized void parseBytecode() {
		if (parsed) {
			// Already parsed, nothing to do
			return;
		}
		
		bytecode = bytecodeProvider.getBytecode(getId());
		if (bytecode == null) {
			//Class is not loaded yet.
			return;
		}
		
		// Don't parse another time
		parsed = true;
		AsmClassVisitor cv = new AsmClassVisitor(this);
		ClassReader cr = null;
		cr = new ClassReader(bytecode);
		
		cr.accept(cv, Attributes.attributes, 0);
		
		//release the bytecode
		bytecode = null;
	}
	
	@Override
	public Collection<String> getBoundBaseClasses() {
		return this.boundBaseClasses;
	}

	/**
	 * Returns the bytecode of this class and cache it temporary.
	 * This method is only needed, if getBytecode of the {@link IBytecodeProvider}
	 * is an expensive operation.
	 * @return
	 */
	protected byte[] allocateAndGetBytecode() {
		if (bytecode == null) {
			bytecode = getBytecode();
		}
		return bytecode;
	}
	
	/**
	 * Get the bytecode directly from the {@link IBytecodeProvider}.
	 * This method can be used, if getBytecode of the {@link IBytecodeProvider}
	 * is not an expensive operation.
	 * @return 
	 */
	@Override
	public byte[] getBytecode() {
		return bytecodeProvider.getBytecode(getId());
	}
	
	/**
	 * Releases the bytecode, if it's cached, an set it in the {@link IBytecodeProvider}
	 */
	protected void releaseBytecode() {
		bytecodeProvider.setBytecode(getId(), bytecode);
		bytecode = null;
	}
	
	/**
	 * Returns the {@link IBytecodeProvider} used for this class
	 * @return
	 */
	protected IBytecodeProvider getBytecodeProvider() {
		return bytecodeProvider;
	}
	
	/**
	 * Sets the bytecode. 
	 * If the bytecode is temporary cached, the cache is used.
	 * Otherwise this method give the bytecode directly to the {@link IBytecodeProvider}
	 * @param bytecode
	 */
	protected void setBytecode(byte[] bytecode) {
		//Is the bytecode temporary cached?
		if (this.bytecode == null) {
			// no, then save the bytecode directly in the bytecode provider
			bytecodeProvider.setBytecode(getId(), bytecode);
		} else {
			// yes, then use the cache
			this.bytecode = bytecode;
		}
	}
	
	public int compare(String callinLabel1, String callinLabel2) {
		for (String[] precedences : this.precedenceses) {
			boolean label1Seen = false, label2Seen = false;
			for (String label : precedences) {
				if (label.equals(callinLabel1)) {
					if (label2Seen)
						return -1; // saw two then one: one has lower priority than two
					label1Seen = true;
				} else if (label.equals(callinLabel2)) {
					if (label1Seen)
						return 1; // saw one then two: one has higher priority than two
					label2Seen = true;
				}
			}
		}
		AbstractBoundClass enclosingClass = getEnclosingClass();
		if (enclosingClass != null) {
			String singleName = getInternalName();
			int pos = singleName.lastIndexOf('$');
			singleName = singleName.substring(pos+1);
			if (singleName.startsWith("__OT__"))
				singleName = singleName.substring("__OT__".length());
			// check for precedence at outer level:
			return enclosingClass.compare(singleName+'.'+callinLabel1, singleName+'.'+callinLabel2);
		}
		return callinLabel1.compareTo(callinLabel2);
	}
}

Back to the top